import React, {useRef, useEffect} from 'react'
import {fromEvent, map, tap, takeUntil, auditTime, concatMap} from 'rxjs'

type IProps = React.InputHTMLAttributes<HTMLInputElement> & React.ClassAttributes<HTMLInputElement>

interface ResizeBarProps extends IProps {
  tagName: 'span' | 'div' | 'i';
  cursor?: string;
  onStart?: (e: MouseEvent) => void;
  onMove?: (e: MouseEvent) => void;
  onEnd?: (e: MouseEvent) => void;
  onChange?: (data: any) => void;
}

const ResizeBar: React.FC<ResizeBarProps> = ({tagName, cursor, onStart, onMove, onEnd, onChange, children, ...props}) => {
  const ref = useRef<any>(null)
  const handleRef = useRef<Pick<ResizeBarProps, 'onStart' | 'onMove' | 'onEnd' | 'onChange' | 'cursor'>>({})

  useEffect(() => {
    handleRef.current = {
      onChange,
      onStart,
      onMove,
      onEnd,
      cursor,
    }
  })

  useEffect(() => {
    const createEl = () => {
      const div = document.createElement('div')
      div.style.position = 'absolute'
      div.style.top = '0px'
      div.style.left = '0px'
      div.style.right = '0px'
      div.style.bottom = '0px'
      div.style.zIndex = '999'
      document.body.appendChild(div)

      return () => {
        document.body.removeChild(div)
      }
    }

    let destory: any = null

    const element = ref.current as HTMLElement;
    const mouseDown$ = fromEvent<MouseEvent>(element, 'mousedown')
      .pipe(tap(e => {
        destory = createEl()
        if(handleRef.current.cursor) {
          document.body.style.cursor = handleRef.current.cursor
        }
        if(handleRef.current.onStart) {
          handleRef.current.onStart(e)
        }
      }));
    const mouseMove$ = fromEvent<MouseEvent>(document, 'mousemove')
      .pipe(tap(e => {
        if(handleRef.current.onMove) {
          handleRef.current.onMove(e)
        }
      }));
    const mouseUp$ = fromEvent<MouseEvent>(document, 'mouseup').pipe(
      tap((e) => {
        e.stopPropagation();
        destory()
        if(handleRef.current.cursor) {
          document.body.style.cursor = ''
        }
        if(handleRef.current.onEnd) {
          handleRef.current.onEnd(e)
        }
      })
    );

    const drag$ = mouseDown$.pipe(
      concatMap((e) => {
        e.stopPropagation();
        const { pageX: startX, pageY: startY } = e;
        let prevX = startX;
        let prevY = startY

        return mouseMove$
          .pipe(takeUntil(mouseUp$))
          .pipe(auditTime(5))
          .pipe(
            map((e) => {
              e.stopPropagation();
              const { pageX: endX, pageY: endY } = e;

              const region = {
                endX,
                endY,
                startX,
                startY,
                moveX: endX - startX,
                moveY: endY - startY,
                currentMoveX: endX - prevX,
                currentMoveY: endY - prevY
              };

              prevX = endX;
              prevY = endY;

              return region
            })
          );
      }),
      tap(res => {
        if(handleRef.current.onChange) {
          handleRef.current.onChange(res)
        }
      })
    );

    const drag = drag$.subscribe()

    return () => {
      drag.unsubscribe()
    }
  }, [])

  return React.createElement(tagName, {ref: ref, ...props}, children)
}

export default ResizeBar
